/*
* Copyright (c) 2012 Kyle Hayward
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#pragma once

#include "TextureSampler.h"

namespace OptixDotNet 
{
	ref class OGLTextureSampler;

	/// <summary>
	/// Type of OpenGl render buffer an <see cref="OptixDotNet::OGLTextureSampler">OGLTextureSampler</see> will be created from.
	/// </summary>
	public enum class GLTarget
	{
		GLTexture2D		= RT_TARGET_GL_TEXTURE_2D,
		GLTextureRect	= RT_TARGET_GL_TEXTURE_RECTANGLE,
		GLTexture3D		= RT_TARGET_GL_TEXTURE_3D,
		GLRenderBuffer	= RT_TARGET_GL_RENDER_BUFFER
	};	

	/// <summary>
	/// Description data used to create an <see cref="OptixDotNet::OGLTextureSampler">OGLTextureSampler</see>.
	/// </summary>
	public value struct OGLTextureSamplerDesc
	{
		/// <summary>
		/// <see cref="OptixDotNet::WrapUVW">Wrap modes</see> for the TextureSampler.
		/// </summary>
		WrapUVW				Wrap;

		/// <summary>
		/// <see cref="OptixDotNet::FilterMinMagMip">Filter modes</see> for the TextureSampler.
		/// </summary>
		FilterMinMagMip		Filter;

		/// <summary>
		/// <see cref="OptixDotNet::TextureReadMode">TextureReadMode</see> for the TextureSampler.<br/>
		/// For ElementType, data returned by a sample will be in the original ElementType range<br/>
		/// For NormalizedFloat, data returned by a sample will be in a normalized [0,1] range
        /// </summary>
		TextureReadMode		Read;

		/// <summary>
		/// <see cref="OptixDotNet::TextureIndexMode">Texture indexing mode</see> for the TextureSampler.
		/// Controls the interpretation of texture coordinates. 
		/// NormalizeCoords will parameterize the texture over [0,1].
		/// ArrayIndex will interpret texture coordinates as array indices into the underlying buffer
        /// </summary>
		TextureIndexMode	Index;

		/// <summary>
		/// Max anisotropy value for anisotropic filtering. A value > 0.0f will enable anisotropic filtering.
        /// </summary>
		float				MaxAnisotropy;

		/// <summary>
		/// Specify the type of <see cref="OptixDotNet::GLTarget">GL resource</see>.
        /// </summary>
		GLTarget			Target;

		/// <summary>
		/// OpenGl texture resource to create the OGLTextureSampler with
        /// </summary>
		unsigned int		Resource;

		inline static OGLTextureSamplerDesc GetDefault( GLTarget target, unsigned int resource )
		{
			OGLTextureSamplerDesc desc = Default;
			desc.Target = target;
			desc.Resource = resource;

			return desc;
		}

		inline static OGLTextureSamplerDesc GetDefault( GLTarget target, unsigned int resource, WrapMode wrapmode )
		{
			WrapUVW wrap = { wrapmode, wrapmode, wrapmode };

			OGLTextureSamplerDesc desc = Default;
			desc.Target = target;
			desc.Resource = resource;
			desc.Wrap = wrap;

			return desc;
		}

		/// <summary>
		/// Gets a default TextureSamplerDesc with settings:
		/// WrapUVW = [ Clamp, Clamp, Clamp ]
		/// Filter = [ Linear, Linear, Linear ]
		/// Read = NormalizedFloat
		/// Index = NormalizeCoords
		/// MaxAnisotropy = 0.0f
		/// Target = GLTexture2D
		/// Resource = 0
		/// </summary>
		static property OGLTextureSamplerDesc Default
		{
			inline OGLTextureSamplerDesc get()
			{
				WrapUVW wrap = { WrapMode::Clamp, WrapMode::Clamp, WrapMode::Clamp };
				FilterMinMagMip filter = { FilterMode::Linear, FilterMode::Linear, FilterMode::None };
				OGLTextureSamplerDesc desc = { wrap, filter, TextureReadMode::NormalizedFloat, TextureIndexMode::NormalizeCoords, 1.0f, GLTarget::GLTexture2D, 0u };
				return desc;
			}
		}
	};

	/// <summary>
	/// Represents a <see cref="OptixDotNet::TextureSampler">TextureSampler</see> created from an OpenGl resource.
	/// <para>Valid OpenGl Resources: GL_TEXTURE_2D, GL_TEXTURE_2D_RECT, GL_TEXTURE_3D, GL_RENDERBUFFER.</para>
	/// <para>Supported render buffer attachement points: GL_COLOR_ATTACHMENT(NUM).</para>
    /// </summary>
	/// <remarks>
	/// <para>
	/// Once a OGLTextureSampler is created the D3D resource is immutable, meaning its properties cannot be modified.
	/// However, respective OpenGl calls can still read/write to the resource
	/// You must Unregister the OGLTextureSampler in order to modify its properties. A following Register must be called.
	/// </para>
	/// Optix automatically detects the size, format, and number of mip map levels of a texture.
	/// Calls to Get/Set Buffer will fail.
	/// <para>
	/// <para><b>SUPPORTED FORMATS:</b></para>
	/// R8I, R8UI, RG8I, RG8UI, RGBA8, RGBA8I, RGBA8UI,<br/>
	/// R16I, R16UI, RG16I, RG16UI, RGBA16, RGBA16I, RGBA16UI,<br/>
	/// R32I, R32UI, RG32I, RG32UI, RGBA32I, RGBA32UI,<br/>
	/// R32F, RG32F, RGBA32F<br/>
	/// </para>
	/// </remarks>
	public ref class OGLTextureSampler : public TextureSampler
	{
	public:
		/// <summary>
		/// Create a TextureSampler from an OpenGl texture resource
		/// </summary>
		/// <param name="context">Optix context</param>
		/// <param name="desc">Description of the TextureSampler</param>
		OGLTextureSampler( Context^ context, OGLTextureSamplerDesc desc );

		/// <summary>
		/// Sets the TextureSampler settings defined in the description
		/// </summary>
		void SetDesc( OGLTextureSamplerDesc% desc );

		/// <summary>
		/// Declares a OpenGl buffer as immutable and accessible by OptiX.
		/// Once registered, the buffer's properties will not be able to be modified
		/// </summary>
		void Register();

		/// <summary>
		/// Declares a OpenGl buffer as mutable and inaccessible by OptiX.
		/// Once unregistered, the buffer's properties are able to be modified.
		/// The buffer must be re-registered once changes are done being made.
		/// </summary>
		void Unregister();

		/// <summary>
		/// Gets a the OpenGl Texture Object ID.
		/// </summary>
		property unsigned int Resource
		{
			unsigned int get();
		}

		/// <summary>
		/// OGLTextureSampler does not support setting a buffer on the sampler.
		/// </summary>
		virtual void SetBuffer( Buffer^ buffer, unsigned int mipLevel ) override { } 

	protected:
		void Create( OGLTextureSamplerDesc% desc );
	};
}